<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My Reader</title>
<script>
var fs = require('fs');
var os = require('os');
</script>
<style>
#indexBox {
	left:50%;
	top:50%;
	width:500px;
	height:400px;
	margin-left:-250px;
	margin-top:-200px;
	position:fixed;
	
	background-color:#555;
	font-size:16px;
	color:#ccc;
	text-align:center;
	padding:5px;
	display: none;
}
#indexTitle {
	padding:10px;
}
#indexBox input {
	width:470px;
	border:1px solid black;
	background: #333;
	color:#ccc;
}
#indexLayer {
	padding:10px;
	text-align:left;
	overflow-y:scroll;
	height:320px;
}
#bookContent {
	padding:20px 70px;
	font-size:20px;
	color:#aaa;
}
a {
	display:block;
	color: #cc0;
	margin-bottom:3px;
	text-decoration:none;
}
a:hover {
	color: red;
}
</style>
</head>
<body style="background-color:black;margin:0;">
<div id="indexBox">
	<div id="indexTitle">
		<div>目录</div>
		<input id="sectionSearch" type="text">
	</div>
	<div id="indexLayer"></div>
</div>
<div id="bookContent">
</div>

<script>
const mkdirp = require('mkdirp');
(function(){
	var bookFile = os.homedir() + '/Documents/Book/盗墓笔记.txt';
	var indexFile = os.homedir() + '/.book/index.json';
	var configFile = os.homedir() + '/.book/config.json';
	var bookFd = null;
	var index;
	var indexLayer = document.getElementById("indexLayer");
	var showIndex = false;
	var config = {"section": 0, "pos": 0};

	mkdirp(os.homedir() + '/.book', function (err) {
		if (err) 
			alert("Create config directory failed: " + err);
	});

	function loadSection(section, begin, end) {
		sectionSearch.value = "";
		config.section = section;
		var buffer = new Buffer(end - begin);
		fs.read(bookFd, buffer, 0, end - begin, begin, function (err, readBytes, data) {
			var bookContent = document.getElementById("bookContent");
			bookContent.innerHTML = data.toString("utf8", 0, readBytes).replace(/    /g, "<span style='display:inline-block;width:40px;'></span>").replace(/\r\n|\n/g, "<br>");
			document.body.scrollTop = config.pos;
		});
	}
	function loadIndex() {
		for (var i = 0; i < index.length; i++) {
			item = index[i];
			var a = document.createElement("a");
			a.id = "section_" + i;
			a.innerHTML = item.name;
			a.href = "javascript:void";
			a.begin = item.begin;
			a.end = item.end;
			a.section = i;
			a.onclick = function(){
				config.pos = 0;
				indexBox.style.display = "none";
				showIndex = false;
				loadSection(this.section, this.begin, this.end);
			};
			indexLayer.appendChild(a);
		}
		// We should load setting from config file.
		if (config.section < index.length)
			loadSection(config.section, index[config.section].begin, index[config.section].end);
	}
	function buildIndex() {
		var buffer = new Buffer(4096);
		var pos = 0;
		var begin = 0;
		var end = 0;
		var topic = null;
		var lineBuffer = new Buffer(4096);
		var lineBufferPos = 0;
		index = [];
		function readBlock() {
			fs.read(bookFd, buffer, 0, 4096, pos, function (err, readBytes, data) {
				if (err) {
					alert(err.message);
					return;
				}
				if (readBytes <= 0) {
					// Finished.
					if (topic && begin !== end) {
						index.push({"name": topic, "begin": begin, "end": end});
						begin = end = (pos + 1);
					}
					loadIndex();
					fs.writeFile(indexFile, JSON.stringify(index), 'utf8', function(err){
						if (err)
							alert(err.message);
					});
					return;
				}
				for (var i = 0; i < readBytes; i++, pos++) {
					if (data[i] === 10 || data[i] === 13) {
						if (lineBufferPos === 0) {
							end = pos;
							continue;
						}
						var srcLine = lineBuffer.toString('utf8', 0, lineBufferPos);
						var line = srcLine.trim();
						lineBufferPos = 0;
						if (/^[^ 　].*$/.test(srcLine)) {
							if (topic && begin !== end) {
								index.push({"name": topic, "begin": begin, "end": end});
								end = begin = end + 1;
							}
							topic = line;
						} else
							end = pos;
					} else {
						lineBuffer[lineBufferPos++] = data[i];
					}
				}
				readBlock();
			});
		}
		readBlock();
	}
	
	// Open book
	fs.open(bookFile, 'r', function(err, fd){
		if (err) {
			alert(err.message);
			return;
		}
		bookFd = fd;
		fs.readFile(configFile, 'utf8', function(err, data) {
			if (!err) {
				config = JSON.parse(data); 
			}
			// Read index file
			fs.readFile(indexFile, 'utf8', function(err, data) {
				if (err) { // No index file existed. 
					buildIndex();
					return;
				}
				// Has existing index file then load it.
				index = JSON.parse(data);
				loadIndex();
			});
		});
	});
	
	window.onbeforeunload = function() {
		if (bookFd)
			fs.close(bookFd);
		fs.writeFile(configFile, JSON.stringify(config), 'utf8', function(err){
			if (err)
				alert(err.message);
		});
	};
	window.onscroll = function(e) {
		config.pos = document.body.scrollTop;
	};
	var bookContent = document.getElementById("bookContent");
	bookContent.onmousedown = function(e) {
		indexBox.style.display = "none";
		showIndex = false;
	};
	window.onkeydown = function(e) {
		if (e.keyCode === 27) { // ESC
			var indexBox = document.getElementById("indexBox");
			if (!showIndex) {
				indexBox.style.display = "block";
				document.getElementById("section_" + config.section).focus();
			} else {
				indexBox.style.display = "none";
			}
			showIndex = !showIndex;
		} else if (e.keyCode === 39){ // RIGHT, PAGE DOWN:34
			if (config.section + 1 < index.length) {
				config.section++;
				config.pos = 0;
				loadSection(config.section, index[config.section].begin, index[config.section].end);
			}
			e.preventDefault();
		} else if (e.keyCode === 37){ // LEFT, PAGE UP:33
			if (config.section > 0) {
				config.section--;
				config.pos = 0;
				loadSection(config.section, index[config.section].begin, index[config.section].end);
			}
			e.preventDefault();
		} else if (e.keyCode === 32) { // SPACE
			//if ()
			if (document.body.scrollTop + document.documentElement.clientHeight >= document.body.scrollHeight) {
				if (config.section + 1 < index.length) {
					config.section++;
					config.pos = 0;
					loadSection(config.section, index[config.section].begin, index[config.section].end);
				}
				e.preventDefault();
			}
		}
		
	};
	var sectionSearch = document.getElementById("sectionSearch");
	var searchPos = null;
	var indexBox = document.getElementById("indexBox");
	indexBox.onkeydown = function(e){
		if (e.keyCode === 114) {
			var content = sectionSearch.value;
			var re = new RegExp(content);
			for (var i = searchPos; i < index.length; i++) {
				if (re.test(index[i].name)) {
					document.getElementById("section_" + i).focus();
					searchPos = i + 1;
					break;
				}
			}
			e.preventDefault();
		}
	};
	sectionSearch.onkeydown = function(e){
		if (e.keyCode === 13) {
			if (!e.ctrlKey)
				searchPos = 0;
			var content = sectionSearch.value;
			var re = new RegExp(content);
			for (var i = searchPos; i < index.length; i++) {
				if (re.test(index[i].name)) {
					document.getElementById("section_" + i).focus();
					searchPos = i + 1;
					break;
				}
			}
			e.preventDefault();
		}
	};
})();

</script>
</body>
</html>
